home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Skunkware 5
/
Skunkware 5.iso
/
src
/
X11
/
unclutter-2.7
/
unclutter.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-06-22
|
11KB
|
332 lines
/*
* unclutter: remove idle cursor image from screen so that it doesnt
* obstruct the area you are looking at.
* doesn't do it if cursor is in root window or a button is down.
* Tries to cope with jitter if you have a mouse that twitches.
* Unfortunately, clients like emacs set different text cursor
* shapes depending on whether they have pointer focus or not.
* Try to kid them with a synthetic EnterNotify event.
* Whereas version 1 did a grab cursor, version 2 creates a small subwindow.
* This may work better with some window managers.
* Some servers return a Visibility event when the subwindow is mapped.
* Sometimes this is Unobscured, or even FullyObscured. Ignore these and
* rely on LeaveNotify events. (An InputOnly window is not supposed to get
* visibility events.)
* Mark M Martin. cetia sep 1992 mmm@cetia.fr
*/
#include <X11/Xos.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xproto.h>
#include <stdio.h>
#include "vroot.h"
char *progname;
pexit(str)char *str;{
fprintf(stderr,"%s: %s\n",progname,str);
exit(1);
}
usage(){
pexit("usage:\n\
-display <display>\n\
-idle <seconds> time between polls to detect idleness.\n\
-jitter <pixels> pixels mouse can twitch without moving\n\
-grab use grabpointer method not createwindow\n\
-reset reset the timer whenever cursor becomes\n\
visible even if it hasn't moved\n\
-root apply to cursor on root window too\n\
-onescreen apply only to given screen of display\n\
-visible ignore visibility events\n\
-noevents dont send pseudo events\n\
-not names... dont apply to windows whose wm-name begins.\n\
(must be last argument)");
}
#define ALMOSTEQUAL(a,b) (abs(a-b)<=jitter)
#define ANYBUTTON (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask)
/* Since the small window we create is a child of the window the pointer is
* in, it can be destroyed by its adoptive parent. Hence our destroywindow()
* can return an error, saying it no longer exists. Similarly, the parent
* window can disappear while we are trying to create the child. Trap and
* ignore these errors.
*/
int (*defaulthandler)();
int errorhandler(display,error)
Display *display;
XErrorEvent *error;
{
if(error->error_code!=BadWindow)
(*defaulthandler)(display,error);
}
char **names; /* -> argv list of names to avoid */
/*
* return true if window has a wm_name and the start of it matches
* one of the given names to avoid
*/
nameinlist(display,window)
Display *display;
Window window;
{
char **cpp;
char *name;
if(names==0)return 0;
if(XFetchName (display, window, &name)){
for(cpp = names;*cpp!=0;cpp++){
if(strncmp(*cpp,name,strlen(*cpp))==0)
break;
}
XFree(name);
return(*cpp!=0);
}
return 0;
}
/*
* create a small 1x1 curssor with all pixels masked out on the given screen.
*/
createnullcursor(display,root)
Display *display;
Window root;
{
Pixmap cursormask;
XGCValues xgc;
GC gc;
XColor dummycolour;
Cursor cursor;
cursormask = XCreatePixmap(display, root, 1, 1, 1/*depth*/);
xgc.function = GXclear;
gc = XCreateGC(display, cursormask, GCFunction, &xgc);
XFillRectangle(display, cursormask, gc, 0, 0, 1, 1);
dummycolour.pixel = 0;
dummycolour.red = 0;
dummycolour.flags = 04;
cursor = XCreatePixmapCursor(display, cursormask, cursormask,
&dummycolour,&dummycolour, 0,0);
XFreePixmap(display,cursormask);
XFreeGC(display,gc);
return cursor;
}
main(argc,argv)char **argv;{
Display *display;
int screen,oldx = -99,oldy = -99,numscreens;
int doroot = 0, jitter = 0, idletime = 5, usegrabmethod = 0, waitagain = 0,
dovisible = 1, doevents = 1, onescreen = 0;
Cursor *cursor;
Window *realroot;
Window root;
char *displayname = 0;
progname = *argv;
argc--;
while(argv++,argc-->0){
if(strcmp(*argv,"-idle")==0){
argc--,argv++;
if(argc<0)usage();
idletime = atoi(*argv);
}else if(strcmp(*argv,"-jitter")==0){
argc--,argv++;
if(argc<0)usage();
jitter = atoi(*argv);
}else if(strcmp(*argv,"-noevents")==0){
doevents = 0;
}else if(strcmp(*argv,"-root")==0){
doroot = 1;
}else if(strcmp(*argv,"-grab")==0){
usegrabmethod = 1;
}else if(strcmp(*argv,"-reset")==0){
waitagain = 1;
}else if(strcmp(*argv,"-onescreen")==0){
onescreen = 1;
}else if(strcmp(*argv,"-visible")==0){
dovisible = 0;
}else if(strcmp(*argv,"-not")==0){
/* take rest of srg list */
names = ++argv;
if(*names==0)names = 0; /* no args follow */
argc = 0;
}else if(strcmp(*argv,"-display")==0 || strcmp(*argv,"-d")==0){
argc--,argv++;
if(argc<0)usage();
displayname = *argv;
}else usage();
}
display = XOpenDisplay(displayname);
if(display==0)pexit("could not open display");
numscreens = ScreenCount(display);
cursor = (Cursor*) malloc(numscreens*sizeof(Cursor));
realroot = (Window*) malloc(numscreens*sizeof(Window));
/* each screen needs its own empty cursor.
* note each real root id so can find which screen we are on
*/
for(screen = 0;screen<numscreens;screen++)
if(onescreen && screen!=DefaultScreen(display)){
realroot[screen] = -1;
cursor[screen] = -1;
}else{
realroot[screen] = XRootWindow(display,screen);
cursor[screen] = createnullcursor(display,realroot[screen]);
}
screen = DefaultScreen(display);
root = VirtualRootWindow(display,screen);
if(!usegrabmethod)
defaulthandler = XSetErrorHandler(errorhandler);
/*
* create a small unmapped window on a screen just so xdm can use
* it as a handle on which to killclient() us.
*/
XCreateWindow(display, realroot[screen], 0,0,1,1, 0, CopyFromParent,
InputOutput, CopyFromParent, 0, (XSetWindowAttributes*)0);
while(1){
Window dummywin,windowin,newroot;
int rootx,rooty,winx,winy;
unsigned int modifs;
Window lastwindowavoided = None;
/* wait for pointer to not move and no buttons down */
while(1){
if(!XQueryPointer(display, root, &newroot, &windowin,
&rootx, &rooty, &winx, &winy, &modifs)){
/* window manager with virtual root may have restarted
* or we have changed screens */
if(!onescreen){
for(screen = 0;screen<numscreens;screen++)
if(newroot==realroot[screen])break;
if(screen>=numscreens)
pexit("not on a known screen");
}
root = VirtualRootWindow(display,screen);
}else if((!doroot && windowin==None) || (modifs & ANYBUTTON) ||
!(ALMOSTEQUAL(rootx,oldx) && ALMOSTEQUAL(rooty,oldy))){
oldx = rootx, oldy = rooty;
}else if(windowin==None){
windowin = root;
break;
}else if(windowin!=lastwindowavoided){
/* descend tree of windows under cursor to bottommost */
Window childin;
int toavoid = xFalse;
lastwindowavoided = childin = windowin;
do{
windowin = childin;
if(nameinlist (display, windowin)){
toavoid = xTrue;
break;
}
}while(XQueryPointer(display, windowin, &dummywin,
&childin, &rootx, &rooty, &winx, &winy, &modifs)
&& childin!=None);
if(!toavoid){
lastwindowavoided = None;
break;
}
}
sleep(idletime);
}
/* wait again next time */
if(waitagain)
oldx = -1-jitter;
if(usegrabmethod){
if(XGrabPointer(display, root, 0,
PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
GrabModeAsync, GrabModeAsync, None, cursor[screen],
CurrentTime)==GrabSuccess){
/* wait for a button event or large cursor motion */
XEvent event;
do{
XNextEvent(display,&event);
}while(event.type==MotionNotify &&
ALMOSTEQUAL(rootx,event.xmotion.x) &&
ALMOSTEQUAL(rooty,event.xmotion.y));
XUngrabPointer(display, CurrentTime);
}
}else{
XSetWindowAttributes attributes;
XEvent event;
Window cursorwindow;
/* create small input-only window under cursor
* as a sub window of the window currently under the cursor
*/
attributes.event_mask = LeaveWindowMask |
EnterWindowMask |
StructureNotifyMask |
FocusChangeMask;
if(dovisible)
attributes.event_mask |= VisibilityChangeMask;
attributes.override_redirect = True;
attributes.cursor = cursor[screen];
cursorwindow = XCreateWindow
(display, windowin,
winx-jitter, winy-jitter,
jitter*2+1, jitter*2+1, 0, CopyFromParent,
InputOnly, CopyFromParent,
CWOverrideRedirect | CWEventMask | CWCursor,
&attributes);
/* discard old events for previously created windows */
XSync(display,True);
XMapWindow(display,cursorwindow);
/*
* Dont wait for expose/map cos override and inputonly(?).
* Check that created window captured the pointer by looking
* for inevitable EnterNotify event that must follow MapNotify.
* [Bug fix thanks to Charles Hannum <mycroft@ai.mit.edu>]
*/
XSync(display,False);
if(!XCheckTypedWindowEvent(display, cursorwindow, EnterNotify,
&event))
oldx = -1-jitter; /* slow down retry */
else{
if(doevents){
/*
* send a pseudo EnterNotify event to the parent window
* to try to convince application that we didnt really leave it
*/
event.xcrossing.type = EnterNotify;
event.xcrossing.display = display;
event.xcrossing.window = windowin;
event.xcrossing.root = root;
event.xcrossing.subwindow = None;
event.xcrossing.time = CurrentTime;
event.xcrossing.x = winx;
event.xcrossing.y = winy;
event.xcrossing.x_root = rootx;
event.xcrossing.y_root = rooty;
event.xcrossing.mode = NotifyNormal;
event.xcrossing.same_screen = True;
event.xcrossing.focus = True;
event.xcrossing.state = modifs;
(void)XSendEvent(display,windowin,
True/*propagate*/,EnterWindowMask,&event);
}
/* wait till pointer leaves window */
do{
XNextEvent(display,&event);
}while(event.type!=LeaveNotify &&
event.type!=FocusOut &&
event.type!=UnmapNotify &&
event.type!=ConfigureNotify &&
event.type!=CirculateNotify &&
event.type!=ReparentNotify &&
event.type!=DestroyNotify &&
(event.type!=VisibilityNotify ||
event.xvisibility.state==VisibilityUnobscured)
);
/* check if a second unclutter is running cos they thrash */
if(event.type==LeaveNotify &&
event.xcrossing.window==cursorwindow &&
event.xcrossing.detail==NotifyInferior)
pexit("someone created a sub-window to my sub-window! giving up");
}
XDestroyWindow(display, cursorwindow);
}
}
}